# | ID | API              | SIP2 ID | FUNCTION                     |
# |----|------------------|---------|------------------------------|
# | 1  | `/login`         | **93**  | 登录系统                      |
# | 2  | `/status`        | **99**  | 查询 SC 状态                  |
# | 3  | `/request_resend`| **97**  | 请求 ACS 重新发送上一条消息     |
# | 4  | `/patron_status` | **23**  | 查询读者状态                  |
# | 5  | `/patron_info`   | **63**  | 查询读者详细信息               |
# | 6  | `/block_patron`  | **01**  | 封锁读者                     |
# | 7  | `/patron_enable` | **25**  | 启用读者                     |
# | 8  | `/item_info`     | **17**  | 查询物品信息                  |
# | 9  | `/checkout`      | **11**  | 借出物品                     |
# | 10 | `/checkin`       | **09**  | 归还物品                     |
# | 11 | `/fee_paid`      | **37**  | 记录读者支付罚款               |
# | 12 | `/end_session`   | **35**  | 结束读者会话                  |
# | 13 | `/hold`          | **15**  | 预约物品                     |
# | 14 | `/renew`         | **29**  | 续借物品                     |
# | 15 | `/renew_all`     | **65**  | 续借所有借阅物品              |

import time
import json
import os
import logging
from logging.handlers import TimedRotatingFileHandler, RotatingFileHandler
from flask import Flask, request, jsonify, render_template
from flask_cors import CORS

from Sip2.sip2 import Sip2

# Create logs directory if it doesn't exist
logs_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logs')
os.makedirs(logs_dir, exist_ok=True)

# Configure logging
def setup_logging():
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    # Create formatters
    detailed_formatter = logging.Formatter(
        '%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(funcName)s() - %(message)s'
    )

    # Console handler (INFO and above)
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(detailed_formatter)
    logger.addHandler(console_handler)

    # File handler with time and size based rotation
    log_file = os.path.join(logs_dir, 'app.log')
    
    # Time-based rotation at midnight
    time_handler = TimedRotatingFileHandler(
        log_file,
        when='midnight',
        interval=1,
        backupCount=30,
        encoding='utf-8'
    )
    time_handler.setLevel(logging.DEBUG)
    time_handler.setFormatter(detailed_formatter)
    logger.addHandler(time_handler)
    
    # Size-based rotation (20MB)
    size_handler = RotatingFileHandler(
        log_file,
        maxBytes=20*1024*1024,  # 20MB
        backupCount=30,
        encoding='utf-8'
    )
    size_handler.setLevel(logging.DEBUG)
    size_handler.setFormatter(detailed_formatter)
    logger.addHandler(size_handler)

    return logger

# Initialize logging
logger = setup_logging()

# Load configuration from .boku_config.json
# with open('./Sip2/.boku_config.json', 'r') as config_file:
#     config = json.load(config_file)

# hostName   = config.get('hostName', '')
# hostPort   = config.get('hostPort', '')
hostName   = "10.10.175.76"
hostPort   = 6788

app = Flask(__name__)
app.secret_key = 'dev'  # Required for flash messages
CORS(app, resources={
    r"/*": {
        "origins": "*",
        "methods": ["GET", "POST", "OPTIONS"],
        "allow_headers": ["Content-Type"]
    }
})



try:
    logger.info("Initializing SIP2 client connection to %s:%s", hostName, hostPort)
    sip_client = Sip2()
    sip_client.hostName = hostName
    sip_client.hostPort = hostPort
    sip_client.tlsEnable = False
    sip_client.connect()
    logger.info("Successfully connected to SIP2 server")
except Exception as e:
    logger.error("Failed to connect to SIP2 server: %s", str(e), exc_info=True)
    sip_client = None



@app.route("/")
def index():
    logger.info("Serving index page")
    return render_template("index.html")



@app.route("/login", methods=["POST"])
def login():
    logger.info("Received login request")
    data = request.json
    loginUserId = data.get("loginUserId")
    loginPassword = data.get("loginPassword", "********")  # Password masked in logs

    logger.debug("Login attempt for user_id: %s", loginUserId)
    
    try:
        msg = sip_client.sip_login_request(loginUserId, loginPassword)  # Password masked in logs
        logger.debug("Sent login request: %s", msg)
        
        response = sip_client.sip_login_response(sip_client.get_response(msg))
        logger.info("Login successful for user_id: %s", loginUserId)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Login failed for user_id %s: %s", loginUserId, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/sc_status", methods=["POST"])
def sc_status():
    logger.info("Received SC status request")
    data = request.json
    scStatusCode = int(data.get("scStatusCode", 0))
    scMaxPrintWidth = data.get("scMaxPrintWidth", "080")
    scProtocolVersion = int(data.get("scProtocolVersion", 2))
    
    logger.debug("SC status parameters - status_code: %d, max_print_width: %s, protocol_version: %d", 
                scStatusCode, scMaxPrintWidth, scProtocolVersion)
    
    try:
        msg = sip_client.sip_sc_status_request(statusCode=scStatusCode, 
                                             maxPrintWidth=scMaxPrintWidth, 
                                             protocolVersion=scProtocolVersion)
        logger.debug("Sent SC status request: %s", msg)
        
        response = sip_client.sip_sc_status_response(sip_client.get_response(msg))
        logger.info("SC status request successful")
        return jsonify({"response": response})
    except Exception as e:
        logger.error("SC status request failed: %s", str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/patron_status", methods=["POST"])
def patron_status():
    logger.info("Received patron status request")
    data = request.json
    patronStatusPatron = data.get("patronStatusPatron")
    patronStatusPatronPwd = data.get("patronStatusPatronPwd", "********")  # Password masked in logs

    logger.debug("Checking status for patron: %s", patronStatusPatron)
    
    try:
        msg = sip_client.sip_patron_status_request(patronStatusPatron, patronStatusPatronPwd)  # Password masked in logs
        logger.debug("Sent patron status request: %s", msg)
        
        response = sip_client.sip_patron_status_response(sip_client.get_response(msg))
        logger.info("Successfully retrieved patron status for: %s", patronStatusPatron)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Failed to get patron status for %s: %s", patronStatusPatron, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/request_resend", methods=["POST"])
def request_resend():
    data = request.json
    requestResendPatron = data.get("requestResendPatron")
    requestResendPatronPwd = data.get("requestResendPatronPwd", "********")  # Password masked in logs
    try:
        msg = sip_client.sip_sc_resend_request(requestResendPatron, requestResendPatronPwd)
    except Exception as e:
        msg = str(e)
    return jsonify({"response": msg})


@app.route("/patron_info", methods=["POST"])
def patron_info():
    logger.info("Received patron info request")
    data = request.json
    patronInfoPatron = data.get("patronInfoPatron")
    patronInfoPatronPwd = data.get("patronInfoPatronPwd", "********")  # Password masked in logs
    patronInfoType = data.get("patronInfoType", "none")
    patronInfoStartItem = data.get("patronInfoStartItem", "1")
    patronInfoEndItem = data.get("patronInfoEndItem", "5")
    
    logger.debug("Patron info parameters - patron: %s, info_type: %s, range: %s-%s", 
                patronInfoPatron, patronInfoType, patronInfoStartItem, patronInfoEndItem)
    
    try:
        msg = sip_client.sip_patron_information_request(
            patronInfoPatron, 
            patronInfoPatronPwd, 
            patronInfoType,
            startItem=patronInfoStartItem, endItem=patronInfoEndItem
        )
        logger.debug("Sent patron information request: %s", msg)
        
        response = sip_client.sip_patron_information_response(sip_client.get_response(msg))
        logger.info("Successfully retrieved patron information for: %s", patronInfoPatron)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Failed to get patron info for %s: %s", patronInfoPatron, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/block_patron", methods=["POST"])
def block_patron():
    logger.info("Received block patron request")
    data = request.json
    blockPatronPatron = data.get("blockPatronPatron")
    blockPatronMsg = data.get("blockPatronMsg", "Blocked by system")
    blockPatronCardRetained = data.get("blockPatronCardRetained", "N")
    
    logger.debug("Block patron parameters - patron: %s, message: %s, card_retained: %s",
                blockPatronPatron, blockPatronMsg, blockPatronCardRetained)
    
    try:
        msg = sip_client.sip_block_patron_request(blockPatronPatron, blockPatronMsg, blockPatronCardRetained)
        logger.debug("Sent block patron request: %s", msg)
        
        response = sip_client.get_response(msg)
        logger.info("Successfully blocked patron: %s", blockPatronPatron)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Failed to block patron %s: %s", blockPatronPatron, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/item_info", methods=["POST"])
def item_info():
    logger.info("Received item info request")
    data = request.json
    itemInfoItemId = str(data.get("itemInfoItemId"))
    
    logger.debug("Requesting information for item: %s", itemInfoItemId)
    
    try:
        time.sleep(0.2)
        msg = sip_client.sip_item_information_request(itemInfoItemId)
        logger.debug("Sent item information request: %s", msg)
        
        response = sip_client.sip_item_information_response(sip_client.get_response(msg))
        logger.info("Successfully retrieved item information for: %s", itemInfoItemId)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Failed to get item info for %s: %s", itemInfoItemId, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/patron_enable", methods=["POST"])
def patron_enable():
    logger.info("Received patron enable request")
    data = request.json
    patronEnablePatron = data.get("patronEnablePatron")
    patronEnablePatronPwd = data.get("patronEnablePatronPwd", "********")  # Password masked in logs

    logger.debug("Enabling patron: %s", patronEnablePatron)
    
    try:
        msg = sip_client.sip_patron_enable_request(patronEnablePatron, patronEnablePatronPwd)  
        logger.debug("Sent patron enable request: %s", msg)
        
        response = sip_client.sip_patron_enable_response(sip_client.get_response(msg))
        logger.info("Successfully enabled patron: %s", patronEnablePatron)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Failed to enable patron %s: %s", patronEnablePatron, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/checkout", methods=["POST"])
def checkout():
    logger.info("Received checkout request")
    data = request.json
    checkoutPatron = data.get("checkoutPatron")
    checkoutPatronPwd = data.get("checkoutPatronPwd", "********")  # Password masked in logs
    checkoutItemId = str(data.get("checkoutItemId"))
    checkoutNbDueDate = data.get("checkoutNbDueDate", "")
    
    logger.debug("Checkout parameters - patron: %s, item_id: %s, due_date: %s",
                checkoutPatron, checkoutItemId, checkoutNbDueDate)
    
    try:
        msg = sip_client.sip_checkout_request(
            checkoutPatron, 
            checkoutPatronPwd, 
            checkoutItemId,  
            checkoutNbDueDate,
            data.get("checkoutScRenewalPolicy", "N"),
            data.get("checkoutItemProperties", ""),
            data.get("checkoutFeeAcknowledged", "N"),
            data.get("checkoutNoBlock", "N"),
            data.get("checkoutCancel", "N")
        )
        logger.debug("Sent checkout request: %s", msg)
        
        response = sip_client.sip_checkout_response(sip_client.get_response(msg))
        logger.info("Successfully checked out item %s to patron %s", checkoutItemId, checkoutPatron)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Checkout failed for item %s, patron %s: %s", 
                    checkoutItemId, checkoutPatron, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/checkin", methods=["POST"])
def checkin():
    logger.info("Received checkin request")
    data = request.json
    checkinItemId = str(data.get("checkinItemId"))
    checkinReturnDate = data.get("checkinReturnDate", None)
    checkinCurrentLocation = data.get("checkinCurrentLocation", "")
    
    logger.debug("Checkin parameters - item_id: %s, return_date: %s, location: %s",
                checkinItemId, checkinReturnDate, checkinCurrentLocation)
    
    try:
        msg = sip_client.sip_checkin_request(
            checkinItemId, 
            checkinReturnDate, 
            checkinCurrentLocation,
            data.get("checkinItemProperties", ""),
            data.get("checkinNoBlock", "N"),
            data.get("checkinCancel", "")
        )
        logger.debug("Sent checkin request: %s", msg)
        
        response = sip_client.sip_checkin_response(sip_client.get_response(msg))
        logger.info("Successfully checked in item: %s", checkinItemId)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Checkin failed for item %s: %s", checkinItemId, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/fee_paid", methods=["POST"])
def fee_paid():
    logger.info("Received fee paid request")
    data = request.json
    feePaidPatron = data.get("feePaidPatron")
    feePaidPatronPwd = data.get("feePaidPatronPwd", "********")  # Password masked in logs
    feePaidFeeType = data.get("feePaidFeeType", "01")
    feePaidFeeAmount = data.get("feePaidFeeAmount", "0.00")
    
    logger.debug("Fee payment parameters - patron: %s, type: %s, amount: %s",
                feePaidPatron, feePaidFeeType, feePaidFeeAmount)
    
    try:
        msg = sip_client.sip_fee_paid_request(
            feePaidPatron, 
            feePaidPatronPwd,  
            feePaidFeeType,
            data.get("feePaidPaymentType", "00"),
            feePaidFeeAmount,
            data.get("feePaidFeeIdentifier", ""),
            data.get("feePaidTransactionId", ""),
            data.get("feePaidCurrencyType", "EUR")
        )
        logger.debug("Sent fee paid request: %s", msg)
        
        response = sip_client.sip_fee_paid_response(sip_client.get_response(msg))
        logger.info("Successfully processed fee payment for patron: %s", feePaidPatron)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Fee payment failed for patron %s: %s", feePaidPatron, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/end_session", methods=["POST"])
def end_session():
    logger.info("Received end session request")
    data = request.json
    endSessionPatron = data.get("endSessionPatron")
    endSessionPatronPwd = data.get("endSessionPatronPwd", "********")  # Password masked in logs
    
    logger.debug("Ending session for patron: %s", endSessionPatron)
    
    try:
        msg = sip_client.sip_end_patron_session_request(endSessionPatron, endSessionPatronPwd) 
        logger.debug("Sent end session request: %s", msg)
        
        response = sip_client.sip_end_patron_session_response(sip_client.get_response(msg))
        logger.info("Successfully ended session for patron: %s", endSessionPatron)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Failed to end session for patron %s: %s", endSessionPatron, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/hold", methods=["POST"])
def hold():
    logger.info("Received hold request")
    data = request.json
    holdPatron = data.get("holdPatron")
    holdPatronPwd = data.get("holdPatronPwd", "********")  # Password masked in logs
    holdMode = data.get("holdMode", "+")
    holdItemId = data.get("holdItemId", "")
    holdTitleId = data.get("holdTitleId", "")
    
    logger.debug("Hold parameters - patron: %s, mode: %s, item: %s, title: %s",
                holdPatron, holdMode, holdItemId, holdTitleId)
    
    try:
        msg = sip_client.sip_hold_request(
            holdPatron, 
            holdPatronPwd,  # Password masked in logs
            holdMode,
            data.get("holdExpirationDate", ""),
            data.get("holdType", ""),
            holdItemId, 
            holdTitleId,
            data.get("holdFeeAcknowledged", "N"),
            data.get("pickupLocation", "")
        )
        logger.debug("Sent hold request: %s", msg)
        
        response = sip_client.sip_hold_response(sip_client.get_response(msg))
        logger.info("Successfully processed hold request for patron: %s, item: %s",
                   holdPatron, holdItemId or holdTitleId)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Hold request failed for patron %s: %s", holdPatron, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/renew", methods=["POST"])
def renew():
    logger.info("Received renew request")
    data = request.json
    renewPatron = data.get("renewPatron")
    renewPatronPwd = data.get("renewPatronPwd", "********")  # Password masked in logs
    renewItemId = data.get("renewItemId", "")
    renewTitleId = data.get("renewTitleId", "")
    
    logger.debug("Renew parameters - patron: %s, item: %s, title: %s",
                renewPatron, renewItemId, renewTitleId)
    
    try:
        msg = sip_client.sip_renew_request(
            renewPatron, 
            renewPatronPwd,  # Password masked in logs
            renewItemId,
            renewTitleId,
            data.get("renewNbDueDate", ""),
            data.get("renewItemProperties", ""),
            data.get("renewFeeAcknowledged", "N"),
            data.get("renewNoBlock", "N"),
            data.get("renewThirdPartyAllowed", "N")
        )
        logger.debug("Sent renew request: %s", msg)
        
        response = sip_client.sip_renew_response(sip_client.get_response(msg))
        logger.info("Successfully renewed item for patron: %s, item: %s",
                   renewPatron, renewItemId or renewTitleId)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Renew failed for patron %s, item %s: %s",
                    renewPatron, renewItemId or renewTitleId, str(e), exc_info=True)
        return jsonify({"response": str(e)})


@app.route("/renew_all", methods=["POST"])
def renew_all():
    logger.info("Received renew all request")
    data = request.json
    renewAllPatron = data.get("renewAllPatron")
    renewAllPatronPwd = data.get("renewAllPatronPwd", "********")  # Password masked in logs
    renewAllFeeAcknowledged = data.get("renewAllFeeAcknowledged", "N")
    
    logger.debug("Renew all parameters - patron: %s, fee_acknowledged: %s",
                renewAllPatron, renewAllFeeAcknowledged)
    
    try:
        msg = sip_client.sip_renew_all_request(renewAllPatron, renewAllPatronPwd, renewAllFeeAcknowledged) 
        logger.debug("Sent renew all request: %s", msg)
        
        response = sip_client.sip_renew_all_response(sip_client.get_response(msg))
        logger.info("Successfully renewed all items for patron: %s", renewAllPatron)
        return jsonify({"response": response})
    except Exception as e:
        logger.error("Renew all failed for patron %s: %s", renewAllPatron, str(e), exc_info=True)
        return jsonify({"response": str(e)})


if __name__ == "__main__":
    logger.info("Starting SIP2 API server on port 5001")
    app.run(debug=False, host="0.0.0.0", port=5001)
